<!-- Vector Stores Tab -->
<div id="tabVectorStores" class="tab-content">
    <div class="endpoint-section" id="vectorStores">
        <h2>
            <span class="endpoint-method get">GET</span>
            <span class="endpoint-path">/api/v1/vector_stores - Vector Stores (OpenAI compatible)</span>
        </h2>
        <p>Manage OpenAI-compatible vector stores backed by the configured adapter (ChromaDB or pgvector).</p>

        <div class="columns">
            <div class="column">
                <h3>Create Vector Store</h3>
                <div class="form-group">
                    <label for="vs_name">Name:</label>
                    <input id="vs_name" placeholder="Optional name">
                </div>
                <div class="form-group">
                    <label for="vs_dimensions">Dimensions:</label>
                    <input id="vs_dimensions" type="number" value="1536" min="1">
                </div>
                <div class="form-group">
                    <label for="vs_model">Embedding Model (metadata):</label>
                    <input id="vs_model" placeholder="text-embedding-3-small">
                </div>
                <button class="api-button" data-action="vs-create">Create Store</button>
            </div>

            <div class="column">
                <h3>Vector Stores</h3>
                <button class="api-button" data-action="vs-list">Refresh</button>
                <ul id="vs_list"></ul>
                <div class="form-group" style="display:flex; gap:8px; align-items:center; margin-top:8px;">
                    <input id="vs_duplicate_name" placeholder="Duplicate as name">
                    <button class="api-button" data-action="vs-duplicate">Duplicate Selected</button>
                    <input id="vs_rename_name" placeholder="Rename selected to" style="margin-left:12px;">
                    <button class="api-button" data-action="vs-rename-from-panel">Rename Selected</button>
                </div>
            </div>
        </div>

        <h3>Edit Store</h3>
        <div class="columns">
            <div class="column">
                <div class="form-group">
                    <label for="vs_edit_id">Store ID:</label>
                    <input id="vs_edit_id" placeholder="vs_...">
                </div>
                <div class="form-group">
                    <label for="vs_edit_name">Name:</label>
                    <input id="vs_edit_name" placeholder="Name">
                </div>
                <div class="form-group">
                    <label for="vs_edit_metadata">Metadata (JSON):</label>
                    <textarea id="vs_edit_metadata" rows="4" placeholder='{"description":"..."}'></textarea>
                </div>
                <button class="api-button" data-action="vs-load">Load</button>
                <button class="api-button" data-action="vs-save">Save</button>
                <button class="api-button btn-danger" data-action="vs-delete">Delete Store</button>
            </div>
        </div>

        <h3>Create/Update From Media</h3>
        <div class="columns">
            <div class="column">
                <div class="form-group">
                    <label for="vs_media_store_name">Store Name (for new store):</label>
                    <input id="vs_media_store_name" placeholder="My Media Store">
                </div>
                <div class="form-group">
                    <label for="vs_media_dimensions">Dimensions:</label>
                    <input id="vs_media_dimensions" type="number" value="1536" min="1">
                </div>
                <div class="form-group">
                    <label for="vs_media_model">Embedding Model (optional):</label>
                    <input id="vs_media_model" placeholder="text-embedding-3-small">
                </div>
                <div class="form-group">
                    <label for="vs_media_ids">Media IDs (comma-separated):</label>
                    <input id="vs_media_ids" placeholder="1,2,3">
                </div>
                <div class="form-group">
                    <label for="vs_media_keywords">Keywords (comma-separated):</label>
                    <input id="vs_media_keywords" placeholder="finance, earnings">
                </div>
                <div class="form-group">
                    <label for="vs_media_keyword_match">Keyword Match:</label>
                    <select id="vs_media_keyword_match">
                        <option value="any" selected>Any (union)</option>
                        <option value="all">All (intersection)</option>
                    </select>
                </div>
                <div class="form-group" style="display:flex; gap:8px; align-items:center;">
                    <button class="api-button" data-action="vs-create-from-media">Create New Store</button>
                    <input id="vs_media_existing_store_id" placeholder="Existing Store ID (vs_...)" style="flex:1;"/>
                    <select id="vs_media_existing_store_select" style="max-width: 260px;">
                        <option value="">-- Select Existing Store --</option>
                    </select>
                    <button class="api-button" data-action="vs-load-stores">Load Stores</button>
                    <button class="api-button" data-action="vs-update-from-media">Update Existing Store</button>
                    <button class="api-button" data-action="vs-rename-selected">Rename Selected</button>
                </div>
                <div class="form-group" style="display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
                    <label for="vs_media_chunk_size">Chunk Size:</label>
                    <input id="vs_media_chunk_size" type="number" value="500" min="50" style="width:100px;">
                    <label for="vs_media_chunk_overlap">Overlap:</label>
                    <input id="vs_media_chunk_overlap" type="number" value="100" min="0" style="width:100px;">
                    <label for="vs_media_chunk_method">Method:</label>
                    <select id="vs_media_chunk_method">
                        <option value="words" selected>words</option>
                        <option value="sentences">sentences</option>
                        <option value="paragraphs">paragraphs</option>
                        <option value="tokens">tokens</option>
                        <option value="semantic">semantic</option>
                    </select>
                    <small style="color: var(--color-text-muted);">Note: when method is 'tokens', Chunk Size is in tokens.</small>
                    <label>
                      <input type="checkbox" id="vs_media_use_existing" /> Use existing media embeddings
                    </label>
                    <label for="vs_media_language">Language:</label>
                    <input id="vs_media_language" placeholder="en" style="width:100px;"/>
                </div>
            </div>
            <div class="column">
                <h4>Result</h4>
                <pre id="vs_media_result">---</pre>
            </div>
        </div>

        <hr>
        <h3>Vectors</h3>
        <div class="columns">
            <div class="column">
                <div class="form-group" style="display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
                    <label for="vs_id">Store ID:</label>
                    <input id="vs_id" placeholder="vs_...">
                    <span id="vs_index_badge" style="padding:2px 6px; border-radius:10px; font-size:12px; background:#ccc; color:#222; display:none;">index: n/a</span>
                    <button class="btn btn-sm btn-secondary" type="button" data-action="vs-refresh-badge">Refresh Badge</button>
                </div>
                <div class="form-group">
                    <label for="vec_text">Content (auto-embed) or Values (JSON array):</label>
                    <textarea id="vec_text" rows="3" placeholder="Enter text to embed..."></textarea>
                    <input id="vec_values" placeholder='[0.1, 0.2, ...]' style="margin-top:8px;">
                </div>
                <button class="api-button" data-action="vs-upsert">Upsert Vector</button>
                <div class="form-group" style="display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
                    <button class="api-button" data-action="vs-list-vectors">List Vectors</button>
                    <label for="vs_limit" style="margin-left:8px;">Page Size:</label>
                    <input id="vs_limit" type="number" value="50" min="1" max="1000" style="width:80px;">
                    <button class="api-button" data-action="vs-prev">Prev</button>
                    <button class="api-button" data-action="vs-next">Next</button>
                    <span id="vs_page_info" style="color: var(--color-text-muted);"></span>
                </div>
                <div class="form-group" style="display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
                    <label for="vs_filter">Filter (JSON):</label>
                    <input id="vs_filter" placeholder='{"$and":[{"genre":"a"},{"score":{"$gte":0.8}}]}' style="min-width:360px; flex:1;">
                    <label for="vs_order_by">Order By:</label>
                    <input id="vs_order_by" placeholder='id or metadata.score' style="width:200px;">
                    <label for="vs_order_dir">Dir:</label>
                    <select id="vs_order_dir"><option>asc</option><option>desc</option></select>
                </div>
                <button class="api-button" data-action="vs-query">Query</button>
                <div class="form-group" style="margin-top:12px;">
                    <label for="vs_bulk">Bulk Upsert (JSON array of records or newline-separated texts):</label>
                    <textarea id="vs_bulk" rows="6" placeholder='[{"id":"1","content":"text"},{"values":[...],"metadata":{}}] OR one text per line'></textarea>
                    <button class="api-button" data-action="vs-bulk-upsert">Bulk Upsert</button>
                </div>
                <div class="form-group">
                    <label for="vec_delete_id">Delete Vector ID:</label>
                    <input id="vec_delete_id" placeholder="vector id">
                    <button class="api-button btn-danger" data-action="vs-delete-vector">Delete Vector</button>
                </div>
                <div class="form-group">
                    <label for="vs_delete_filter">Delete By Filter (JSON):</label>
                    <input id="vs_delete_filter" placeholder='{"media_id":"42"}'>
                    <button class="api-button btn-danger" data-action="vs-delete-by-filter">Delete by Filter</button>
                </div>
            </div>
            <div class="column">
                <h4>Result</h4>
                <pre id="vs_result">---</pre>
                <div id="vs_vector_list"></div>
            </div>
        </div>
        <hr>
        <h3>Admin (Index & Tuning)</h3>
        <div class="columns">
            <div class="column">
                <div class="form-group" style="display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
                    <label for="vs_admin_id">Store ID:</label>
                    <input id="vs_admin_id" placeholder="vs_..." style="width:240px;">
                    <button class="api-button" data-action="vs-admin-index-info">Index Info</button>
                    <button class="btn btn-sm btn-secondary" type="button" data-action="vs-refresh-badge">Refresh Badge</button>
                </div>
                <div class="form-group" style="display:flex; gap:8px; align-items:center;">
                    <label for="vs_ef_search">ef_search:</label>
                    <input id="vs_ef_search" type="number" value="64" min="1" style="width:120px;">
                    <button class="api-button" data-action="vs-admin-set-ef">Set ef_search</button>
                    <small style="color: var(--color-text-muted);">Note: ef_search applies to pgvector only; ignored for Chroma.</small>
                </div>
                <div class="form-group" style="display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
                    <label for="vs_index_type">Index Type:</label>
                    <select id="vs_index_type"><option>hnsw</option><option>ivfflat</option><option>drop</option></select>
                    <label for="vs_index_metric">Metric:</label>
                    <select id="vs_index_metric"><option>cosine</option><option>euclidean</option><option>ip</option></select>
                    <label for="vs_index_m">M:</label>
                    <input id="vs_index_m" type="number" value="16" min="2" style="width:100px;">
                    <label for="vs_index_efc">ef_construction:</label>
                    <input id="vs_index_efc" type="number" value="200" min="1" style="width:120px;">
                    <label for="vs_index_lists">lists (IVF):</label>
                    <input id="vs_index_lists" type="number" value="100" min="1" style="width:100px;">
                    <button class="api-button btn-danger" data-action="vs-admin-rebuild">Rebuild Index</button>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
async function vsCreate(){
  const name = document.getElementById('vs_name').value.trim();
  const dim = parseInt(document.getElementById('vs_dimensions').value || '1536');
  const model = document.getElementById('vs_model').value.trim();
  const body = { dimensions: dim };
  if (name) body.name = name;
  if (model) body.embedding_model = model;
  const res = await apiClient.post('/api/v1/vector_stores', body);
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
  await vsList();
}

async function vsList(){
  const res = await apiClient.get('/api/v1/vector_stores');
  const ul = document.getElementById('vs_list');
  ul.innerHTML = '';
  (res.data || []).forEach(s => {
    const li = document.createElement('li');
    li.innerHTML = `${s.name} (<code>${s.id}</code>) - dim=${s.dimensions} ` +
                   `<button class="btn-small" onclick="vsRenameQuick('${s.id}')">Rename</button>` +
                   ` <button class="btn-small" onclick="vsSelect('${s.id}','${s.name.replace(/"/g,'&quot;')}')">Select</button>`;
    // Add inline index-type badge and manual refresh button
    try {
      const rowBadge = document.createElement('span');
      rowBadge.id = `vs_row_badge_${s.id}`;
      rowBadge.className = 'vs-row-badge';
      rowBadge.style.cssText = 'margin-left:6px; padding:2px 6px; border-radius:10px; font-size:12px; background:#eee; color:#444;';
      rowBadge.textContent = 'index: …';
      li.appendChild(rowBadge);
      const refreshBtn = document.createElement('button');
      refreshBtn.className = 'btn-small';
      refreshBtn.textContent = 'Refresh';
      refreshBtn.onclick = (ev) => { ev.stopPropagation(); vsRefreshRowBadge(s.id); };
      li.appendChild(refreshBtn);
      // Lazy-load badge on first hover
      li.addEventListener('mouseenter', () => {
        const badgeEl = document.getElementById(`vs_row_badge_${s.id}`);
        if (badgeEl && !badgeEl.dataset.loaded) {
          vsFetchIndexBadgeForStore(s.id, badgeEl).catch(()=>{});
        }
      }, { once: true });
    } catch {}
    li.style.cursor = 'pointer';
    li.onclick = () => {
      document.getElementById('vs_edit_id').value = s.id;
      document.getElementById('vs_edit_name').value = s.name || '';
      document.getElementById('vs_edit_metadata').value = JSON.stringify(s.metadata || {}, null, 2);
      document.getElementById('vs_id').value = s.id;
    };
    ul.appendChild(li);
  });
}

async function vsLoadStore(){
  const id = document.getElementById('vs_edit_id').value.trim();
  if(!id){ alert('Enter store id'); return; }
  const res = await apiClient.get(`/api/v1/vector_stores/${encodeURIComponent(id)}`);
  document.getElementById('vs_edit_name').value = res.name || '';
  document.getElementById('vs_edit_metadata').value = JSON.stringify(res.metadata || {}, null, 2);
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
}

async function vsSaveStore(){
  const id = document.getElementById('vs_edit_id').value.trim();
  if(!id){ alert('Enter store id'); return; }
  let md = {};
  const name = document.getElementById('vs_edit_name').value.trim();
  const mdText = document.getElementById('vs_edit_metadata').value.trim();
  if (mdText){
    try { md = JSON.parse(mdText); } catch(e){ alert('Invalid metadata JSON'); return; }
  }
  const payload = {};
  if (name) payload.name = name;
  payload.metadata = md;
  const res = await apiClient.patch(`/api/v1/vector_stores/${encodeURIComponent(id)}`, payload);
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
  await vsList();
}

async function vsDeleteStore(){
  const id = document.getElementById('vs_edit_id').value.trim();
  if(!id){ alert('Enter store id'); return; }
  if(!confirm('Delete this vector store?')) return;
  const res = await apiClient.delete(`/api/v1/vector_stores/${encodeURIComponent(id)}`);
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
  await vsList();
}

async function vsUpsertVector(){
  const id = document.getElementById('vs_id').value.trim();
  if(!id){ alert('Enter vector store id'); return; }
  const text = document.getElementById('vec_text').value.trim();
  const valsStr = document.getElementById('vec_values').value.trim();
  let record = {};
  if (text) {
    record.content = text;
  } else if (valsStr) {
    try { record.values = JSON.parse(valsStr); } catch(e){ alert('Invalid values JSON'); return; }
  } else { alert('Provide content or values'); return; }
  const body = { records: [record] };
  const res = await apiClient.post(`/api/v1/vector_stores/${encodeURIComponent(id)}/vectors`, body);
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
}

async function vsListVectors(){
  const id = document.getElementById('vs_id').value.trim();
  if(!id){ alert('Enter vector store id'); return; }
  const limit = parseInt(document.getElementById('vs_limit').value || '50');
  const offset = window.__vs_offset || 0;
  const query = { limit, offset };
  const filterText = (document.getElementById('vs_filter')?.value || '').trim();
  if (filterText){
    try { JSON.parse(filterText); query.filter = filterText; } catch(e){ alert('Invalid filter JSON'); return; }
  }
  const orderBy = (document.getElementById('vs_order_by')?.value || '').trim();
  const orderDir = (document.getElementById('vs_order_dir')?.value || 'asc').trim();
  if (orderBy){ query.order_by = orderBy; }
  if (orderDir){ query.order_dir = orderDir; }
  const res = await apiClient.get(`/api/v1/vector_stores/${encodeURIComponent(id)}/vectors`, query);
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
  renderVectorList(res.data || []);
  const pageInfo = document.getElementById('vs_page_info');
  if (pageInfo) {
    const total = res.pagination?.total ?? 0;
    const end = Math.min((offset + (res.data||[]).length), total);
    pageInfo.textContent = total ? `Showing ${offset+1}-${end} of ${total}` : '';
  }
}

async function vsQuery(){
  const id = document.getElementById('vs_id').value.trim();
  if(!id){ alert('Enter vector store id'); return; }
  const q = prompt('Enter query text (leave blank to cancel)');
  if (q === null) return;
  const body = { query: q, top_k: 5 };
  const res = await apiClient.post(`/api/v1/vector_stores/${encodeURIComponent(id)}/query`, body);
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
}

async function vsAdminIndexInfo(){
  const id = (document.getElementById('vs_admin_id')?.value || document.getElementById('vs_id')?.value || '').trim();
  if (!id){ alert('Enter store id'); return; }
  const res = await apiClient.get(`/api/v1/vector_stores/${encodeURIComponent(id)}/admin/index_info`);
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
  vsRenderIndexBadge(res);
}

async function vsAdminSetEfSearch(){
  const value = parseInt(document.getElementById('vs_ef_search')?.value || '64');
  const res = await apiClient.post('/api/v1/vector_stores/admin/hnsw_ef_search', { ef_search: value });
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
}

async function vsAdminRebuildIndex(){
  const id = (document.getElementById('vs_admin_id')?.value || document.getElementById('vs_id')?.value || '').trim();
  if (!id){ alert('Enter store id'); return; }
  const index_type = (document.getElementById('vs_index_type')?.value || 'hnsw');
  const metric = (document.getElementById('vs_index_metric')?.value || 'cosine');
  const m = parseInt(document.getElementById('vs_index_m')?.value || '16');
  const ef_construction = parseInt(document.getElementById('vs_index_efc')?.value || '200');
  const lists = parseInt(document.getElementById('vs_index_lists')?.value || '100');
  // Styled danger modal instead of confirm
  const notes = `
    <div style="border-left:4px solid var(--color-error); padding:8px 12px; background: var(--color-surface-alt); margin-bottom:10px;">
      <div style="font-weight:600; color: var(--color-error); margin-bottom:6px;">Danger: Rebuild Index</div>
      <div style="color: var(--color-text-secondary);">
        This operation will drop and recreate the ANN index for <code>${Utils.escapeHtml(id)}</code>.<br>
        During rebuild, query performance may degrade. For large datasets, consider running off-peak.<br>
        Settings:<br>
        • Type: <b>${Utils.escapeHtml(index_type)}</b> • Metric: <b>${Utils.escapeHtml(metric)}</b><br>
        • HNSW → m=<b>${m}</b>, ef_construction=<b>${ef_construction}</b> • IVFFLAT → lists=<b>${lists}</b>
      </div>
    </div>
  `;
  const footer = `
    <button class="btn btn-secondary" type="button" id="rb_cancel">Cancel</button>
    <button class="btn btn-danger" type="button" id="rb_confirm">Rebuild Index</button>
  `;
  const modal = new Modal({ title: 'Confirm Rebuild', content: notes, size: 'medium', footer });
  modal.show();
  const onConfirm = async () => {
    try {
      const res = await apiClient.post(`/api/v1/vector_stores/${encodeURIComponent(id)}/admin/rebuild_index`, { index_type, metric, m, ef_construction, lists });
      document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
      vsRenderIndexBadge(res);
      try { vsRefreshRowBadge(id); } catch {}
      if (typeof Toast !== 'undefined') Toast.success('Index rebuild started');
    } catch(e){
      if (typeof Toast !== 'undefined') Toast.error(`Rebuild failed: ${e?.message||e}`);
    } finally {
      modal.close();
    }
  };
  const onCancel = () => modal.close();
  modal.modal.querySelector('#rb_confirm').onclick = onConfirm;
  modal.modal.querySelector('#rb_cancel').onclick = onCancel;
}

async function vsUpdateIndexBadgeFromId(){
  const id = (document.getElementById('vs_admin_id')?.value || document.getElementById('vs_id')?.value || '').trim();
  const badge = document.getElementById('vs_index_badge');
  if (!id || !badge){ return; }
  try {
    const res = await apiClient.get(`/api/v1/vector_stores/${encodeURIComponent(id)}/admin/index_info`);
    vsRenderIndexBadge(res);
  } catch(e) { badge.style.display = 'none'; }
}

function vsRenderIndexBadge(info){
  const badge = document.getElementById('vs_index_badge');
  if (!badge) return;
  const t = (info.index_type || info.backend || 'n/a').toString().toLowerCase();
  let text = `index: ${t}`;
  if (info.ops) text += ` (${info.ops})`;
  badge.textContent = text;
  badge.style.display = 'inline-block';
  let bg = '#ccc', fg = '#222';
  if (t === 'hnsw') { bg = '#d1f2d1'; fg = '#195c19'; }
  else if (t === 'ivfflat') { bg = '#ffe0b3'; fg = '#5c3c19'; }
  else if (t === 'managed') { bg = '#d9e7ff'; fg = '#123c7a'; }
  badge.style.background = bg;
  badge.style.color = fg;
}

async function vsFetchIndexBadgeForStore(storeId, badgeEl){
  try {
    if (!badgeEl) badgeEl = document.getElementById(`vs_row_badge_${storeId}`);
    if (!badgeEl) return;
    badgeEl.textContent = 'index: …';
    const res = await apiClient.get(`/api/v1/vector_stores/${encodeURIComponent(storeId)}/admin/index_info`);
    const t = (res.index_type || 'n/a').toString().toLowerCase();
    const backend = (res.backend || 'n/a').toString().toLowerCase();
    let text = `index: ${t} • backend: ${backend}`;
    if (res.ops) text += ` (${res.ops})`;
    badgeEl.textContent = text;
    let bg = '#eee', fg = '#444';
    if (t === 'hnsw') { bg = '#d1f2d1'; fg = '#195c19'; }
    else if (t === 'ivfflat') { bg = '#ffe0b3'; fg = '#5c3c19'; }
    else if (t === 'managed') { bg = '#d9e7ff'; fg = '#123c7a'; }
    badgeEl.style.background = bg; badgeEl.style.color = fg;
    badgeEl.dataset.loaded = '1';
  } catch (e){
    if (badgeEl) { badgeEl.textContent = 'index: n/a'; badgeEl.style.background = '#eee'; badgeEl.style.color = '#888'; }
  }
}

function vsRefreshRowBadge(storeId){
  const badgeEl = document.getElementById(`vs_row_badge_${storeId}`);
  if (badgeEl) { delete badgeEl.dataset.loaded; }
  vsFetchIndexBadgeForStore(storeId, badgeEl);
}

async function vsBulkUpsert(){
  const id = document.getElementById('vs_id').value.trim();
  if(!id){ alert('Enter vector store id'); return; }
  const text = document.getElementById('vs_bulk').value.trim();
  if(!text){ alert('Enter bulk payload'); return; }
  let records = [];
  try {
    // Try parse JSON array
    const maybe = JSON.parse(text);
    if (Array.isArray(maybe)) {
      records = maybe;
    } else {
      throw new Error('not array');
    }
  } catch(e){
    // Fallback: treat as newline-separated texts
    records = text.split('\n').map(line => ({ content: line.trim() })).filter(r => r.content);
  }
  if (!records.length){ alert('No records to upsert'); return; }
  const res = await apiClient.post(`/api/v1/vector_stores/${encodeURIComponent(id)}/vectors`, { records });
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
}

async function vsDeleteVector(){
  const storeId = document.getElementById('vs_id').value.trim();
  const vecId = document.getElementById('vec_delete_id').value.trim();
  if (!storeId || !vecId){ alert('Enter store id and vector id'); return; }
  const res = await apiClient.delete(`/api/v1/vector_stores/${encodeURIComponent(storeId)}/vectors/${encodeURIComponent(vecId)}`);
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
}

async function vsDeleteByFilter(){
  const storeId = document.getElementById('vs_id').value.trim();
  if (!storeId){ alert('Enter store id'); return; }
  const txt = (document.getElementById('vs_delete_filter')?.value || '').trim();
  if (!txt){ alert('Enter filter JSON'); return; }
  let filt = {};
  try { filt = JSON.parse(txt); } catch(e){ alert('Invalid filter JSON'); return; }
  const res = await apiClient.post(`/api/v1/vector_stores/${encodeURIComponent(storeId)}/admin/delete_by_filter`, { filter: filt });
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
}

function renderVectorList(items){
  const container = document.getElementById('vs_vector_list');
  if (!container) return;
  if (!items.length){ container.innerHTML = '<div class="muted">No vectors on this page.</div>'; return; }
  const storeId = document.getElementById('vs_id').value.trim();
  const rows = items.map(it => {
    const md = (it.metadata && Object.keys(it.metadata).length) ? JSON.stringify(it.metadata) : '';
    const content = (it.content || '').slice(0, 100).replace(/</g,'&lt;');
    return `<div class="list-row" style="display:flex; gap:8px; align-items:center; border-bottom:1px solid var(--color-border); padding:6px 0;">
      <code style="flex:0 0 260px; overflow:hidden; text-overflow:ellipsis;">${it.id}</code>
      <span style="flex:1 1 auto; color: var(--color-text-muted);">${content}</span>
      <span style="flex:0 0 200px; color: var(--color-text-muted);">${md}</span>
      <button class="btn-small btn-danger" onclick="vsDeleteVectorInline('${encodeURIComponent(storeId)}','${encodeURIComponent(it.id)}')">Delete</button>
    </div>`;
  }).join('');
  container.innerHTML = `<div style="margin-top:8px;">${rows}</div>`;
}

async function vsDeleteVectorInline(encStoreId, encVecId){
  const storeId = decodeURIComponent(encStoreId);
  const vecId = decodeURIComponent(encVecId);
  if(!confirm(`Delete vector ${vecId}?`)) return;
  const res = await apiClient.delete(`/api/v1/vector_stores/${encodeURIComponent(storeId)}/vectors/${encodeURIComponent(vecId)}`);
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
  // Refresh current page
  await vsListVectors();
}

async function vsPrevPage(){
  const current = window.__vs_offset || 0;
  const limit = parseInt(document.getElementById('vs_limit').value || '50');
  window.__vs_offset = Math.max(0, current - limit);
  await vsListVectors();
}

async function vsNextPage(){
  const id = document.getElementById('vs_id').value.trim();
  if(!id){ alert('Enter vector store id'); return; }
  const limit = parseInt(document.getElementById('vs_limit').value || '50');
  const offset = window.__vs_offset || 0;
  // Peek next page availability using pagination.next_offset from last result if available
  // Fallback: optimistic increment
  window.__vs_offset = offset + limit;
  await vsListVectors();
}

async function vsRenameQuick(id){
  const name = prompt('New name for store', '');
  if (name === null) return;
  const res = await apiClient.patch(`/api/v1/vector_stores/${encodeURIComponent(id)}`, { name });
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
  await vsList();
}

function vsSelect(id, name){
  const idInput = document.getElementById('vs_id');
  if (idInput) idInput.value = id;
  const existingSel = document.getElementById('vs_media_existing_store_select');
  const existingId = document.getElementById('vs_media_existing_store_id');
  if (existingSel){ existingSel.value = id; }
  if (existingId){ existingId.value = id; }
  const dupName = document.getElementById('vs_duplicate_name');
  if (dupName){ dupName.value = `${name}-copy`; }
}

async function vsDuplicateSelected(){
  const id = document.getElementById('vs_id').value.trim();
  if (!id){ alert('Select a store first (click on list item or use Select button)'); return; }
  const new_name = document.getElementById('vs_duplicate_name').value.trim();
  if (!new_name){ alert('Enter a name for the duplicate'); return; }
  const res = await apiClient.post(`/api/v1/vector_stores/${encodeURIComponent(id)}/duplicate`, { new_name });
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
  await vsList();
}

async function vsRenameSelectedFromPanel(){
  const id = document.getElementById('vs_id').value.trim();
  if (!id){ alert('Select a store first'); return; }
  const new_name = document.getElementById('vs_rename_name').value.trim();
  if (!new_name){ alert('Enter a new name'); return; }
  const res = await apiClient.patch(`/api/v1/vector_stores/${encodeURIComponent(id)}`, { name: new_name });
  document.getElementById('vs_result').textContent = JSON.stringify(res, null, 2);
  await vsList();
}

// Create/Update Vector Store from Media helpers
async function uiCreateStoreFromMedia(){
  const name = document.getElementById('vs_media_store_name')?.value.trim();
  if (!name){ alert('Enter store name'); return; }
  const dimensions = parseInt(document.getElementById('vs_media_dimensions')?.value || '1536');
  const model = document.getElementById('vs_media_model')?.value.trim();
  const ids = (document.getElementById('vs_media_ids')?.value || '').split(',').map(s=>parseInt(s.trim())).filter(n=>!isNaN(n));
  const kws = (document.getElementById('vs_media_keywords')?.value || '').split(',').map(s=>s.trim()).filter(Boolean);
  const kwMatch = (document.getElementById('vs_media_keyword_match')?.value || 'any');
  const chunk_size = parseInt(document.getElementById('vs_media_chunk_size')?.value || '500');
  const chunk_overlap = parseInt(document.getElementById('vs_media_chunk_overlap')?.value || '100');
  const chunk_method = (document.getElementById('vs_media_chunk_method')?.value || 'words');
  const use_existing_embeddings = document.getElementById('vs_media_use_existing')?.checked || false;
  const language = (document.getElementById('vs_media_language')?.value || '').trim() || undefined;
  const body = { store_name: name, dimensions, embedding_model: model || undefined, media_ids: ids.length?ids:undefined, keywords: kws.length?kws:undefined, keyword_match: kwMatch, chunk_size, chunk_overlap, chunk_method, language, use_existing_embeddings };
  const res = await apiClient.post('/api/v1/vector_stores/create_from_media', body);
  const out = document.getElementById('vs_media_result'); if (out) out.textContent = JSON.stringify(res, null, 2);
}

async function uiUpdateStoreFromMedia(){
  let existing = document.getElementById('vs_media_existing_store_id')?.value.trim();
  const sel = document.getElementById('vs_media_existing_store_select');
  if (!existing && sel && sel.value) existing = sel.value;
  if (!existing){ alert('Enter existing store id'); return; }
  const name = document.getElementById('vs_media_store_name')?.value.trim();
  const dimensions = parseInt(document.getElementById('vs_media_dimensions')?.value || '1536');
  const model = document.getElementById('vs_media_model')?.value.trim();
  const ids = (document.getElementById('vs_media_ids')?.value || '').split(',').map(s=>parseInt(s.trim())).filter(n=>!isNaN(n));
  const kws = (document.getElementById('vs_media_keywords')?.value || '').split(',').map(s=>s.trim()).filter(Boolean);
  const kwMatch = (document.getElementById('vs_media_keyword_match')?.value || 'any');
  const chunk_size = parseInt(document.getElementById('vs_media_chunk_size')?.value || '500');
  const chunk_overlap = parseInt(document.getElementById('vs_media_chunk_overlap')?.value || '100');
  const chunk_method = (document.getElementById('vs_media_chunk_method')?.value || 'words');
  const use_existing_embeddings = document.getElementById('vs_media_use_existing')?.checked || false;
  const language = (document.getElementById('vs_media_language')?.value || '').trim() || undefined;
  const body = { store_name: name || existing, dimensions, embedding_model: model || undefined, media_ids: ids.length?ids:undefined, keywords: kws.length?kws:undefined, keyword_match: kwMatch, chunk_size, chunk_overlap, chunk_method, language, use_existing_embeddings, update_existing_store_id: existing };
  const res = await apiClient.post('/api/v1/vector_stores/create_from_media', body);
  const out = document.getElementById('vs_media_result'); if (out) out.textContent = JSON.stringify(res, null, 2);
}

async function uiLoadExistingStores(){
  const sel = document.getElementById('vs_media_existing_store_select');
  if (!sel) return;
  const res = await apiClient.get('/api/v1/vector_stores');
  const list = (res.data || []);
  sel.innerHTML = '<option value="">-- Select Existing Store --</option>' +
      list.map(s => `<option value="${s.id}">${s.name} (${s.id})</option>`).join('');
}

async function uiRenameSelectedStore(){
  let existing = document.getElementById('vs_media_existing_store_id')?.value.trim();
  const sel = document.getElementById('vs_media_existing_store_select');
  if (!existing && sel && sel.value) existing = sel.value;
  if (!existing){ alert('Select existing store'); return; }
  const newName = (document.getElementById('vs_media_store_name')?.value || '').trim();
  if (!newName){ alert('Enter a new store name in the Store Name field'); return; }
  const res = await apiClient.patch(`/api/v1/vector_stores/${encodeURIComponent(existing)}`, { name: newName });
  alert('Renamed successfully');
  try { await uiLoadExistingStores(); } catch(e) {}
}
</script>

<!-- Vector Batches Tab -->
<div id="tabVectorBatches" class="tab-content">
  <div class="endpoint-section">
    <h2>
      <span class="endpoint-method get">GET</span>
      <span class="endpoint-path">/api/v1/vector_stores/batches - Recent Vector Batches</span>
    </h2>
    <div class="card" style="padding:10px; border:1px solid var(--color-border); border-radius:6px; margin-bottom:10px;">
      <h3 style="margin:0 0 8px 0;">Admin: Discover Users</h3>
      <div class="form-group" style="display:flex; gap:8px; align-items:center; flex-wrap: wrap;">
        <button class="api-button" data-action="vb-load-users">Load Users</button>
        <select id="vb_user_select" style="min-width:220px;">
          <option value="">-- Select User --</option>
        </select>
        <small style="color: var(--color-text-muted);">Select a user to populate the filter below.</small>
      </div>
      <div id="vb_users_info" class="muted"></div>
    </div>
    <div class="form-group" style="display:flex; gap:8px; align-items:center; flex-wrap: wrap;">
      <label for="vb_status">Status:</label>
      <select id="vb_status">
        <option value="">All</option>
        <option value="processing">Processing</option>
        <option value="completed">Completed</option>
        <option value="failed">Failed</option>
      </select>
      <label for="vb_limit">Limit:</label>
      <input id="vb_limit" type="number" value="50" min="1" max="500" style="width:100px;"/>
      <label for="vb_offset">Offset:</label>
      <input id="vb_offset" type="number" value="0" min="0" style="width:100px;"/>
      <label for="vb_user">User ID (admin):</label>
      <input id="vb_user" type="text" placeholder="user id" style="width:160px;"/>
      <button class="api-button" data-action="vb-refresh">Refresh</button>
    </div>
    <div id="vb_list"></div>
    <pre id="vb_result">---</pre>
  </div>
</div>

<script>
async function vbList(){
  const status = document.getElementById('vb_status').value;
  const limit = parseInt(document.getElementById('vb_limit').value||'50');
  const offset = parseInt(document.getElementById('vb_offset').value||'0');
  const user = document.getElementById('vb_user').value.trim();
  const query = {};
  if (status) query.status = status;
  query.limit = limit; query.offset = offset;
  // Backend currently defaults to current user; user override is admin-only and a future enhancement
  const res = await apiClient.get('/api/v1/vector_stores/batches', query);
  document.getElementById('vb_result').textContent = JSON.stringify(res, null, 2);
  const list = document.getElementById('vb_list');
  const rows = (res.data||[]).map(r=>{
    const err = r.error? `<span style='color:var(--color-danger)'>${(r.error||'').slice(0,120)}</span>`:'';
    return `<div class='list-row' style='display:flex; gap:8px; border-bottom:1px solid var(--color-border); padding:6px 0;'>
      <code style='flex:0 0 220px;'>${r.id}</code>
      <code style='flex:0 0 220px;'>${r.store_id}</code>
      <span style='flex:0 0 80px;'>${r.status}</span>
      <span style='flex:0 0 80px;'>${r.upserted}</span>
      <span style='flex:1 1 auto; color:var(--color-text-muted);'>${err}</span>
    </div>`;
  }).join('');
  list.innerHTML = `<div style='margin-top:8px;'>${rows || '<div class="muted">No batches found</div>'}</div>`;
}

async function vbLoadUsers(){
  const info = document.getElementById('vb_users_info');
  const sel = document.getElementById('vb_user_select');
  try {
    const res = await apiClient.get('/api/v1/vector_stores/admin/users');
    const rows = res.data || [];
    if (!rows.length){ info.textContent = 'No users found.'; return; }
    sel.innerHTML = '<option value="">-- Select User --</option>' +
      rows.map(u => `<option value="${u.user_id}">${u.user_id} (stores: ${u.store_count}, batches: ${u.batch_count})</option>`).join('');
    sel.onchange = () => {
      const val = sel.value;
      const vbUser = document.getElementById('vb_user');
      if (vbUser) vbUser.value = val;
    };
    info.textContent = `Loaded ${rows.length} users.`;
  } catch (e){
    info.textContent = `Failed to load users: ${e.message || e}`;
  }
}
</script>
